package com.xhwl.centiotdata.handler.eesd.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xhwl.centiotdata.constant.EesdConstant;
import com.xhwl.centiotdata.handler.eesd.EesdFactory;
import com.xhwl.centiotdata.handler.eesd.EesdHandler;
import com.xhwl.centiotdata.mq.producer.GeneralProducer;
import com.xhwl.centiotdata.pojo.enums.eesd.EesdMethodEnum;
import com.xhwl.centiotdata.service.cent.IBiEnvCentService;
import com.xhwl.centiotdata.service.cent.ICentAlarmService;
import com.xhwl.centiotdata.service.cent.IDeviceCentService;
import com.xhwl.centiotdata.service.cent.IEesdCentService;
import com.xhwl.centiotdata.suppliersystem.eesd.iotnorth.IotNorthHandler;
import com.xhwl.centiotdata.utils.RedisUtils;
import com.xhwl.centiotdata.utils.ThreadPoolExecutorService;
import com.xhwl.common.constant.cent.KeyConstant;
import com.xhwl.common.constant.cent.alarm.AlarmConstant;
import com.xhwl.common.constant.cent.eesd.EesdTopicConstant;
import com.xhwl.common.dto.cent.device.DeviceDTO;
import com.xhwl.common.dto.cent.eesd.*;
import com.xhwl.common.dto.cent.energy.EesdElectricDetailDTO;
import com.xhwl.common.enums.device.DeviceOnlineStatusEnum;
import com.xhwl.common.enums.eesd.IotProductIdEnum;
import com.xhwl.common.enums.iot.IotEventTypeEnum;
import com.xhwl.common.exception.BusinessException;
import com.xhwl.common.pojo.cent.alarm.AlarmManagement;
import com.xhwl.common.pojo.cent.bi.OdsEnvironmentMetrics;
import com.xhwl.common.pojo.cent.device.Device;
import com.xhwl.common.pojo.cent.device.DeviceProjSupplierCategory;
import com.xhwl.common.query.cent.device.DevicePageQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author： guan
 * @Description： eesd系统-对接iot北向接口
 * @Date 2021/09/06/ 11:31
 */
@Slf4j
@Service
public class EesdIotNorthHandler extends EesdHandler {
    @Resource
    private IotNorthHandler iotNorthHandler;
    @Resource
    private IBiEnvCentService biEnvCentService;
    @Resource
    private ICentAlarmService  centAlarmService;
    @Resource
    private IDeviceCentService deviceCentService;
    @Autowired
    private GeneralProducer generalProducer;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    public RedisTemplate redisTemplate;
    @Autowired
    IEesdCentService iEesdCentService;



    /**
     * 10：兴海-iot平台
     */
    @Override
    public void afterPropertiesSet() {
        // 兴海-iot平台
        EesdFactory.register(EesdMethodEnum.IOT_NORTH.getId(), this);
    }

    /**
     * 环境同步原始数据/实时数据/设备状态数据到设备中台
     * @param config 10-兴海物联iot
     */
    @Override
    public void pullEnvData(DeviceProjSupplierCategory config) {
        try {
            List<OdsEnvironmentMetricsDTO> originalList = iotNorthHandler.getOriginalData(config);
            if (CollectionUtils.isEmpty(originalList)) {
                log.info("数据为空环境不同步");
                return;
            }

            List<EnvIotNorthDeviceDataDTO> realTimeData = iotNorthHandler.getRealTimeData(config);
            String realTimeDataStr = JSONObject.toJSONString(realTimeData);
            log.info("环境同步实时数据 开始： {}", realTimeData.size());
            biEnvCentService.pushEnvRealTimeData(realTimeDataStr);

            List<Device> list = iotNorthHandler.getUpdateOnlineStatus(config);
            //log.info("环境同步原始设备状态数据到设备中台 开始： {}", JSONObject.toJSONString(list));
            deviceCentService.asyncUpdateOnlineStatus(list);
            log.info("环境同步原始数据/实时数据/设备状态数据到设备中台 结束");
        } catch (Exception e) {
            log.error("环境同步原始数据/实时数据/设备状态数据到设备中台 失败： {}", e.getMessage());
        }
    }

    /**
     * 环境同步原始数据到设备中台
     * @param config
     */
    @Override
    public void pushEnvToDeviceData(DeviceProjSupplierCategory config) {
        try {
            List<DeviceDTO> list = iotNorthHandler.getPushEnvToDeviceData(config);
            if (CollectionUtils.isEmpty(list)) {
                log.info("数据为空环境不同步");
                return;
            }
            //log.info("环境同步原始数据到设备中台 开始： {}", JSONObject.toJSONString(list));
            deviceCentService.asyncUpdateDevice(list);
            log.info("环境同步原始数据到设备中台 结束");
        } catch (Exception e) {
            log.error("环境同步原始数据到设备中台 失败： {}", e.getMessage());
        }
    }

    /**
     * 环境原始数据推送给告警中台
     * @param config
     */
    @Override
    public void pushEnvData(DeviceProjSupplierCategory config) {
        try {
            List<OdsEnvironmentMetricsDTO> originalList = iotNorthHandler.getOriginalData(config);
            if (CollectionUtils.isEmpty(originalList)) {
                log.info("数据为空环境不同步");
                return;
            }
            String dataStr = JSONObject.toJSONString(originalList);
            //log.info("环境原始数据推送给告警中台 开始： {}", dataStr);
            for (OdsEnvironmentMetricsDTO odsEnvironmentMetricsDTO : originalList) {
                OdsEnvironmentMetrics odsEnvironmentMetrics = new OdsEnvironmentMetrics();
                BeanUtils.copyProperties(odsEnvironmentMetricsDTO, odsEnvironmentMetrics);
                centAlarmService.iotEnvironmentReceive(JSONObject.toJSONString(odsEnvironmentMetrics));
            }
            log.info("环境原始数据推送给告警中台 结束");
        } catch (Exception e) {
            log.error("环境原始数据推送给告警中台 失败： {}",  e.getMessage());
        }
    }

    /**
     * 环境同步历史数据
     * @param config
     */
    @Override
    public void pullEnvHourData(DeviceProjSupplierCategory config) {
        try {
            List<OdsEnvironmentMetricsDTO> originalData = iotNorthHandler.getOriginalData(config);
            if (CollectionUtils.isEmpty(originalData)) {
                log.info("数据为空环境不同步");
                return;
            }
            String dataStr = JSONObject.toJSONString(originalData);
            biEnvCentService.pushEnvHourData(dataStr);
            log.info("环境同步历史数据 结束：{}", originalData.size());
        } catch (Exception e) {
            log.error("环境同步历史数据 失败： {}",  e.getMessage());
        }
    }




















    /**********************************************兴海-IOT***************************************************/

    /**
     * 同步设备属性值
     * @param config 10-兴海物联iot
     */
    @Override
    public void pullIotNorthData(DeviceProjSupplierCategory config) {
        IotProductIdEnum iotProductIdEnum = Optional.ofNullable(config)
                .map(DeviceProjSupplierCategory::getProperties)
                .map(c -> c.getString(EesdConstant.EESD_DEVICE_TYPE_CODE))
                .map(c -> IotProductIdEnum.getByType(c)).get();
        log.info("-------iotProductIdEnum:{}",iotProductIdEnum);
        switch (iotProductIdEnum) {
            case ICE_STORAGE_AIR :
                List<IceStorageAirDTO> list1 = iotNorthHandler.getIceStorageAirData(config);
                generalProducer.send(EesdTopicConstant.ICE_STORAGE_AIR_TOPIC, list1);
                break;
            case COOLING_SCREW :
                List<CoolingScrewDTO> list2 = iotNorthHandler.getCoolingScrewData(config);
                generalProducer.send(EesdTopicConstant.COOLING_SCREW_TOPIC, list2);
                break;
            case WATT_HOUR_METER :
                List<WattHourMeterDTO> list3 = iotNorthHandler.getWattHourMeterData(config);
                generalProducer.send(EesdTopicConstant.WATT_HOUR_METER_TOPIC, list3);
                break;
            case YDZJ_WATT_METER:
            case YDZJ_WATER_METER:
                List<EesdElectricDetailDTO> electricList = iotNorthHandler.getIotDataByCodeV2(config);
                log.info("同步电表设备明细接口数据：{}", electricList);
                iEesdCentService.baDeviceDetailSync(electricList);
                // 同步能耗仪表树
                if (!CollectionUtils.isEmpty(electricList)) {
                    // get会强制转换成post请求
                    log.info("同步能耗仪表树开始:{}", electricList.size());
                    iEesdCentService.energyTreeSync(electricList.get(0));
                }
            default:
        }
    }

    /**
     * 同步iot设备数据到设备中台
     * @param config
     */
    @Override
    public void pullIotNorthDevices(DeviceProjSupplierCategory config) {
        List<Device> deviceList = syncDeviceByType(config);
        if (deviceList == null) return;
        List<DeviceDTO> list = new ArrayList<>();
        for (Device device : deviceList) {
            DeviceDTO dto = new DeviceDTO();
            dto.setThirdDeviceId(device.getThirdDeviceId());
            dto.setName(device.getName());
            dto.setOnlineStatus(device.getOnlineStatus().id);
            dto.setDeviceTypeId(device.getDeviceTypeId());
            dto.setProjectId(device.getProjectId());
            dto.setProductId(device.getProductId());
            dto.setAttributeJson(config.getProperties());
            list.add(dto);
        }
        log.info("兴海iot同步设备开始:{}", JSONObject.toJSONString(list));
        deviceCentService.asyncUpdateDevice(list);

        List<String> deviceIds = list.stream().map(DeviceDTO::getThirdDeviceId).collect(Collectors.toList());
        // 更新
        ThreadPoolExecutorService.getThreadPool().execute(() -> mapThirdDeviceId(config.getProjectId(), config.getProductId(), deviceIds));
    }


    /**
     * 同步iot设备状态数据到设备中台
     * @param config
     */
    @Override
    public void pullIotNorthDeviceStatus(DeviceProjSupplierCategory config) {
        List<Device> deviceList = syncDeviceByType(config);
        if (deviceList == null) return;
        deviceCentService.asyncUpdateOnlineStatus(deviceList);
    }

    private List<Device> syncDeviceByType(DeviceProjSupplierCategory config) {
        int isSyncDeviceFlag = Optional.ofNullable(config.getProperties().getInteger("isSyncFlag")).orElse(0);
        if (isSyncDeviceFlag == 0) {
            return null;
        }
        List<Device> deviceList = iotNorthHandler.devices(config);
        if (CollectionUtils.isEmpty(deviceList)) {
            return null;
        }
        Integer typeId = Optional.ofNullable(config.getProperties().getString(EesdConstant.EESD_DEVICE_TYPE_CODE))
                .map(c -> IotProductIdEnum.getByType(c).getCode()).get();
        deviceList.stream().filter(d -> d.getDeviceTypeId().equals(typeId)).collect(Collectors.toList());
        return deviceList;
    }


    /**
     * 同步统计数据
     * @param config
     */
    @Override
    public void iotNorthStatistics(DeviceProjSupplierCategory config) {
        biEnvCentService.syncIceStorageAirMinute(config.getProjectId());
    }


    /**
     * 同步iot设备水电表数据到设备中台
     * @param config
     */
    @Override
    public void pullIotNorthYDZJDevices(DeviceProjSupplierCategory config) {
        List<Device> deviceList = iotNorthHandler.getDeviceListV2(config);
        if (CollectionUtils.isEmpty(deviceList)) {
            return;
        }
        List<DeviceDTO> list = new ArrayList<>();
        deviceList.stream().forEach(device -> {
            DeviceDTO dto = new DeviceDTO();
            // 使用原始封装状态
            dto.setOnlineStatus(device.getOnlineStatus().getId());
            // 如果状态不等于1 则是离线 获取设备影子中的属性进行校验 更正
            if (DeviceOnlineStatusEnum.ONLINE.getId() != dto.getOnlineStatus()) {
                Map<String, Object> attr = iotNorthHandler.getDeviceAttribute(device.getNodeId());
                if (Objects.nonNull(attr) && attr.containsKey("online") && "true".equals(attr.get("online"))) {
                    // 设备中台会对 0 和 1 改为 1 离线 和 2 在线
                    dto.setOnlineStatus(DeviceOnlineStatusEnum.ONLINE.getId());
                }
            }
            dto.setThirdDeviceId(device.getThirdDeviceId());
            dto.setName(device.getName());
            dto.setId(device.getId());
            dto.setDeviceTypeId(device.getDeviceTypeId());
            dto.setNodeId(device.getNodeId());
            dto.setProjectId(device.getProjectId());
            dto.setProductId(device.getProductId());
            dto.setAttributeJson(config.getProperties());
            list.add(dto);
        });
        log.info("兴海iot同步水电表设备开始:{}", JSONObject.toJSONString(list));
        deviceCentService.asyncUpdateDeviceV2(list);
        List<String> deviceIds = list.stream().map(DeviceDTO::getThirdDeviceId).collect(Collectors.toList());
        // 更新
        ThreadPoolExecutorService.getThreadPool().execute(() -> mapThirdDeviceId(config.getProjectId(), config.getProductId(), deviceIds));
    }




    /**
     * 消费兴海-iot告警信息
     * @param record
     */
    @KafkaListener(topics = EesdTopicConstant.IOT_V1_EVENT_UPLOAD_TOPIC)
    public void onIotNorthAlarmMsg(ConsumerRecord record) {
        try {
            log.info("=============================>消费兴海-iot告警信息开始消费<==============================================");
            Object value = Optional.ofNullable(record).map(ConsumerRecord::value)
                    .orElseThrow(() -> new BusinessException("消费兴海-iot告警信息异常"));
            log.info("EESD-兴海-iot告警推送数据：{}", JSONObject.toJSONString(value.toString()));
            // todo 暂定南投为2(与映射设备id有点冲突)
            Integer projectId = 2;
            String key = new StringBuilder(KeyConstant.EESD_DEVICE_IDS_MAP).append(projectId).append(":").append(EesdMethodEnum.IOT_NORTH.getId()).toString();
            Map<String, String> codeMap = redisTemplate.opsForHash().entries(RedisUtils.format(EesdMethodEnum.IOT_NORTH.getId()));
            log.info("EESD-兴海-iot告警映射关系：{}", JSONObject.toJSONString(codeMap));
            JSONObject jsonPara = JSONObject.parseObject(value.toString());
            String deviceKey = jsonPara.getString("src");
            JSONObject para = JSONObject.parseObject(jsonPara.getString("payload"));
            //String parameters = para.getString("parameters");
            String eventCode = para.getString("id");
            String description = IotEventTypeEnum.getDescription(eventCode);
            /*Map<String, String> map = JSONObject.parseObject(parameters, Map.class);
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String v = entry.getValue();
                sb.append(v).append("。");
            }*/
            Date date = new Date();
            Object o = redisUtils.hmGet(key, deviceKey);
            DeviceDTO deviceDTO;
            if (o == null) {
                log.error("消费兴海-iot告警信息获取设备中台信息异常");
                return;
            } else {
                deviceDTO = JSONObject.parseObject(o.toString(), DeviceDTO.class);
            }
            AlarmManagement alarmManagement = new AlarmManagement();
            alarmManagement.setProjectId(Optional.ofNullable(deviceDTO).map(DeviceDTO::getProjectId).get());
            alarmManagement.setDeviceId(Optional.ofNullable(deviceDTO).map(DeviceDTO::getId).get());
            alarmManagement.setAlarmInventoryCode(codeMap.get(eventCode));
            alarmManagement.setContent(description);
            alarmManagement.setAlarmTime(date);
            log.info("推送iotNorth数据推送给告警中台:{}", JSONObject.toJSONString(alarmManagement));
            generalProducer.send(AlarmConstant.ALARM_TOPIC, alarmManagement);
        } catch (Exception e) {
            log.error("异常：{}", e.getMessage());
        }
    }


    /*****************************************处理设备************************************************/

    public void mapThirdDeviceId(Integer projectId, Integer productId, List<String> deviceIds) {
        DevicePageQuery devicePageQuery = new DevicePageQuery();
        devicePageQuery.setProjectId(projectId);
        devicePageQuery.setProductId(productId);
        devicePageQuery.setThirdDeviceIdList(deviceIds);
        Page<DeviceDTO> devicePage = deviceCentService.pageDevice(null, devicePageQuery);
        if (!CollectionUtils.isEmpty(devicePage.getRecords())) {
            log.info("EESD查询设备中台设备处理设备数据：{}", JSONObject.toJSONString(devicePage.getRecords()));
            String key = new StringBuilder(KeyConstant.EESD_DEVICE_IDS_MAP).append(projectId).append(":").append(productId).toString();
            List<DeviceDTO> records = devicePage.getRecords();
            if (!CollectionUtils.isEmpty(records)) {
                for (DeviceDTO record : records) {
                    String hashKey = record.getThirdDeviceId();
                    redisTemplate.opsForHash().put(key, hashKey, JSONObject.toJSONString(record));
                    redisTemplate.expire(key, 15, TimeUnit.MINUTES);
                }
            }
        }
    }
}
